前端权限设计方案
概述
前端权限控制是后台管理系统的核心安全机制。本节系统梳理前端权限的分级体系,从按钮级权限到页面级权限的完整设计方案,并明确前端权限的边界——前端权限仅是 UI 层面的控制,真正的安全保障需要服务端配合。
前端权限层级
┌─────────────────────────────────────────────┐
│ 页面权限 (Page) │
│ ┌──────────────────────────────────────┐ │
│ │ 路由/菜单权限 (Route/Menu) │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ 按钮/组件权限 (Button) │ │ │
│ │ └──────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
text
权限层级说明
| 层级 | 控制对象 | 实现方式 |
|---|---|---|
| 按钮/组件权限 | 单个按钮或组件的显示/隐藏 | v-has 指令 |
| 菜单/路由权限 | 用户可访问的页面菜单 | 路由守卫 + 动态菜单 |
| 页面权限 | 整个页面的访问控制 | 路由守卫重定向 |
按钮级权限(Button Permission)
实现方式
<!-- 使用 v-has 指令控制按钮显示 -->
<el-button v-has.role="['admin']">删除用户</el-button>
<el-button v-has.permission="['system:user:edit']">编辑</el-button>
vue
原理
// 指令根据用户权限从 DOM 中移除无权限的元素
if (!shouldShow) {
el.parentNode?.removeChild(el) // 物理移除,非 CSS 隐藏
}
typescript
菜单/路由权限(Menu/Route Permission)
实现方式
// router/guard.ts
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
// 白名单路由
if (to.meta.whiteList) {
return next()
}
// 检查用户是否有访问该路由的权限
const requiredRoles = to.meta.roles as string[] || []
if (requiredRoles.length === 0) {
return next()
}
const hasPermission = requiredRoles.some(role =>
userStore.roles.includes(role)
)
if (!hasPermission) {
return next('/403') // 无权限跳转到 403 页面
}
next()
})
typescript
动态菜单生成
// 根据 API 返回的权限数据动态生成路由
function generateRoutes(serverRoutes: ServerRoute[]): RouteRecordRaw[] {
return serverRoutes
.filter(route => hasPermission(route.meta?.roles))
.map(route => transformRoute(route))
}
// 将生成的路由添加到 router
router.addRoute(layoutRoute)
typescript
页面权限(Page Permission)
用户访问 /system/user
→ 路由守卫检查
→ 有权限 → 渲染页面
→ 无权限 → 重定向 /403
→ 未登录 → 重定向 /login
text
前端权限的局限性
| 局限 | 说明 |
|---|---|
| 假权限 | 前端代码明文可修改,用户可通过 DevTools 绕过 |
| 仅隐藏 UI | 隐藏按钮不等于阻止操作 |
| 依赖服务端 | 必须配合服务端接口权限验证 |
安全边界
前端权限:控制 UI 显示,提升用户体验
↓ 配合
服务端权限:控制接口访问,保障数据安全
text
完整权限方案架构
// 前后端配合的完整权限方案
// 1. 登录后获取权限数据
const { token, roles, permissions } = await loginApi(credentials)
userStore.setToken(token)
userStore.setRoles(roles)
userStore.setPermissions(permissions)
// 2. 前端路由守卫(UI 层)
router.beforeEach(async (to) => {
if (!userStore.token) return '/login'
if (!userStore.roles.length) {
await userStore.getUserInfo() // 获取权限
// 动态添加路由
}
})
// 3. 前端按钮权限(UI 层)
// <el-button v-has.role="['admin']">删除</el-button>
// 4. 服务端接口权限(安全层)
// API 请求携带 token → 服务端验证权限 → 返回数据或 403
typescript
实践要点
- 前端权限分为三级:按钮级、菜单/路由级、页面级
- 前端权限本质是"假权限",只控制 UI 显示,不保障真正的安全
- 服务端必须配合进行接口级别的权限验证(操作权限)
- 按钮权限使用
v-has指令,路由权限使用路由守卫 - 前端权限 + 服务端权限 = 完整的安全方案
↑